/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.flexengine.map;

import com.flexengine.engine.Finalizable;
import com.flexengine.engine.Initializable;
import com.flexengine.engine.Priority;
import com.flexengine.engine.Renderable;
import com.flexengine.engine.Updatable;
import com.flexengine.input.InputManager;
import com.flexengine.logger.Logger;
import com.flexengine.scene.SceneBundle;
import com.flexengine.view.GameWindow;
import com.flexengine.view.Graphics;
import com.flexengine.view.Texture;
import com.flexengine.xml.Attribute;
import com.flexengine.xml.Element;
import com.flexengine.xml.Root;
import java.awt.Insets;
import java.io.Serializable;

/**
 * @author Luann R. Athayde
 * @version 1.0
 * @since 1.0
 */
@Root
public class GameMap implements Renderable, Updatable, Initializable, Finalizable, Serializable {

    public static final int DEFAULT_MAP_WIDTH  = 64;
    public static final int DEFAULT_MAP_HEIGHT = 64;
    public static final int MAX_MAP_WIDTH  = 256;
    public static final int MAX_MAP_HEIGHT = 256;
    
    public static final int SCROLL_SPEED[] = {10,9,8,7,6,5,4,3,2,1};
    
    @Attribute
    private String     name;
    @Element
    private String     description;
    @Element
    private Texture    loadTexture;
    
    @Element
    private final Tile tiles[][];
    @Attribute
    private final int  mapWidth;
    @Attribute
    private final int  mapHeight;
    
    private transient Tile[][]   viewPortTiles;
    private transient int        viewPortX;
    private transient int        viewPortY;
    private transient int        viewPortWidth;
    private transient int        viewPortHeight;
    private transient int        viewScrollSize;
    
    private transient Insets     insets;
    
    @Element
    private boolean    mouseScrollEnable = true;
    @Element
    private int        mouseScrollSpeed = 10;
    private transient int        mouseScrollCount  = 0;
    
    @Element
    private boolean    leftKeyScrollEnable  = true;
    @Element
    private boolean    rightKeyScrollEnable = true;
    @Element
    private boolean    topKeyScrollEnable   = true;
    @Element
    private boolean    downKeyScrollEnable  = true;
    @Element
    private int        keyScrollSpeed       = 3;
    private transient int        keyScrollCount       = 0;

    public GameMap() {
        this("GameMap", DEFAULT_MAP_WIDTH, DEFAULT_MAP_HEIGHT);
    }
    public GameMap(String name, int mapWidth, int mapHeight) {
        Logger.logMessage("Creating a GAME MAP!");
        this.name = name;
        this.description = null;
        this.loadTexture = null;
        if( mapWidth<=0 || mapWidth>MAX_MAP_WIDTH ) {
            mapWidth = DEFAULT_MAP_WIDTH;
        }
        if( mapHeight<=0 || mapHeight>MAX_MAP_HEIGHT ) {
            mapHeight = DEFAULT_MAP_HEIGHT;
        }
        this.mapWidth = mapWidth;
        this.mapHeight = mapHeight;
        this.tiles = new Tile[mapWidth][mapHeight];
        this.viewScrollSize = 20;
        // -- insets...
        Logger.logMessage("GAME MAP CREATED!");
    }
    public GameMap(String name) {
        this(name, DEFAULT_MAP_WIDTH,DEFAULT_MAP_HEIGHT);
    }
    
    @Override
    public void update(Long time, Long totalTime) {
        // -- move view port...
        mouseScrollCount++;
        if( mouseScrollCount==mouseScrollSpeed && mouseScrollEnable ) {
            if( InputManager.getMouseX() <= viewScrollSize+insets.left ) {
                scrollLeft();
                updateViewPortTiles();
            }
            if( InputManager.getMouseX()>=GameWindow.getInstance().getWidth()-(viewScrollSize+insets.right) ) {
                scrollRight();
                updateViewPortTiles();
            }
            if( InputManager.getMouseY() <= viewScrollSize+insets.top ) {
                scrollTop();
                updateViewPortTiles();
            }
            if( InputManager.getMouseY()>=GameWindow.getInstance().getHeight()-(viewScrollSize+insets.bottom) ) {
                scrollDown();
                updateViewPortTiles();
            }
            mouseScrollCount = 0;
        }
        
        keyScrollCount++;
        if( keyScrollCount == keyScrollSpeed ) {
            if( InputManager.isKeyDown(InputManager.VK_LEFT) && leftKeyScrollEnable ) {
                scrollLeft();
                updateViewPortTiles();
                InputManager.releaseKey(InputManager.VK_LEFT);
            }
            if( InputManager.isKeyDown(InputManager.VK_RIGHT) && rightKeyScrollEnable ) {
                scrollRight();
                updateViewPortTiles();
                InputManager.releaseKey(InputManager.VK_RIGHT);
            }
            if( InputManager.isKeyDown(InputManager.VK_UP) && topKeyScrollEnable ) {
                scrollTop();
                updateViewPortTiles();
                InputManager.releaseKey(InputManager.VK_UP);
            }
            if( InputManager.isKeyDown(InputManager.VK_DOWN) && downKeyScrollEnable ) {
                scrollDown();
                updateViewPortTiles();
                InputManager.releaseKey(InputManager.VK_DOWN);
            }
            keyScrollCount = 0;
        }
        
        /* Debug use only...
        if( InputManager.isKeyDown(InputManager.VK_Z) ) {
            mouseScrollSpeed++;
            mouseScrollCount = 0;
            if(mouseScrollSpeed>10) {
                mouseScrollSpeed = 10;
            }
            InputManager.releaseKey(InputManager.VK_Z);
        }
        if( InputManager.isKeyDown(InputManager.VK_X) ) {
            mouseScrollSpeed--;
            mouseScrollCount = 0;
            if( mouseScrollSpeed<1) {
                mouseScrollSpeed = 1;
            }
            InputManager.releaseKey(InputManager.VK_X);
        } */
        
        // --
        for(int i=0;i<viewPortWidth;i++) {
            for(int j=0;j<viewPortHeight;j++) {
                Tile tile = viewPortTiles[i][j];
                if( tile!=null ) {
                    tile.update(time, totalTime);
                }
            }
        }
    }
    
    @Override
    public void render(Graphics graphics) {
        for(int i=0;i<viewPortWidth;i++) {
            for(int j=0;j<viewPortHeight;j++) {
                Tile tile = viewPortTiles[i][j];
                if( tile!=null && tile.isVisible() ) {
                    tile.render(graphics);
                }
            }
        }
        // -- debug info...
        /*
        graphics.setColor(Color.ORANGE);
        graphics.drawRect(
                viewScrollSize+insets.left, viewScrollSize+insets.top,
                GameWindow.getInstance().getWidth()-insets.right-viewScrollSize*2, 
                GameWindow.getInstance().getHeight()-insets.bottom-insets.top-viewScrollSize*2);
        
        graphics.setColor(Color.BLACK);
        graphics.fillRect(0, 0, 150, 90);
        graphics.setColor(Color.RED);
        graphics.drawRect(0, 0, 150, 90);
        graphics.drawString("ViewPort Size = ("+viewPortWidth+","+viewPortHeight+")", 10, 40);
        graphics.drawString("ViewPort Loc  = ("+viewPortX+","+viewPortY+")", 10, 60);
        graphics.drawString("Scroll Speed  = "+mouseScrolllSpeed, 10, 80);
        */
    }

    @Override
    public void init(SceneBundle bundle) {
        insets = GameWindow.getInstance().getInsets();
        // -- teste porpouse...
        for(int i=0;i<mapWidth;i++) {
            for(int j=0;j<mapHeight;j++) {
                tiles[i][j] = new Tile();
            }
        }
        //tiles[64][64].setTexture(TextureManager.getInstance().get("ball", "textures/ball.png", 2));
        // --
        updateViewPort();
    }

    @Override
    public void end(SceneBundle bundle) {
        
    }

    public void updateViewPortTiles() {
        for(int i=viewPortX,vi=0; i<(viewPortX+viewPortWidth); i++, vi++) {
            for(int j=viewPortY, vj=0; j<(viewPortY+viewPortHeight); j++, vj++) {
                Tile tile = tiles[i][j];
                if(tile!=null) {
                    tile.setLocation(vi*Tile.TILE_WIDTH+insets.left, vj*Tile.TILE_HEIGHT+insets.top);
                }
                viewPortTiles[vi][vj] = tile;
            }
        }
    }
    
    protected void updateViewPort() {
        Logger.logMessage("GameMap - updating view port...");
        viewPortWidth = GameWindow.getInstance().getWidth()/Tile.TILE_WIDTH; // 32
        viewPortHeight = GameWindow.getInstance().getHeight()/Tile.TILE_HEIGHT; // 18
        viewPortX = mapWidth/2 - viewPortWidth/2; // 48
        viewPortY = mapHeight/2 - viewPortHeight/2; // 55
        
        viewPortTiles = new Tile[viewPortWidth][viewPortHeight];
        
        for(int i=viewPortX,vi=0; i<(viewPortX+viewPortWidth); i++, vi++) {
            for(int j=viewPortY, vj=0; j<(viewPortY+viewPortHeight); j++, vj++) {
                Tile tile = tiles[i][j];
                if(tile!=null) {
                    tile.setLocation(vi*Tile.TILE_WIDTH+insets.left, vj*Tile.TILE_HEIGHT+insets.top);
                }
                viewPortTiles[vi][vj] = tile;
            }
        }
        
        Logger.logMessage("GameMap - VIEW PORT UPDATED!");
    }
    
    public Tile getTileAt(int i, int j) {
        return tiles[i][j];
    }
    
    public void setTileAt(Tile tile, int i, int j) {
        tiles[i][j] = tile;
    }
    
    @Override
    public Priority getPriority() {
        return Priority.LOW_PRIORITY;
    }

    @Override
    public boolean isVisible() {
        return true;
    }
    
    /**
     * <p>1 - Very slow...
     * <p>10 - Very fast...
     * @param mouseScrolllSpeed 
     */
    public void setMouseScrollSpeed(int mouseScrolllSpeed) {
        if( mouseScrolllSpeed>=1 && mouseScrolllSpeed<=10 ) {
            mouseScrolllSpeed--;
            this.mouseScrollSpeed = SCROLL_SPEED[mouseScrolllSpeed];
        }
    }
    
    /**
     * 1 - Very slow...
     * 10 - Very fast...
     * @param keyScrollSpeed 
     */
    public void setKeyScrollSpeed(int keyScrollSpeed) {
        if( keyScrollSpeed>=1 && keyScrollSpeed<=10 ) {
            keyScrollSpeed--;
            this.keyScrollSpeed = SCROLL_SPEED[mouseScrollSpeed];
        }
    }
    
    public void scrollTop() {
        viewPortY--;
        if( viewPortY < 0 ) {
            viewPortY = 0;
        }
    }
    public void scrollDown() {
        viewPortY++;
        if( viewPortY > mapHeight-viewPortHeight ) {
            viewPortY = mapHeight-viewPortHeight;
        }
    }
    public void scrollLeft() {
        viewPortX--;
        if( viewPortX<0 ) {
            viewPortX = 0;
        }
    }
    public void scrollRight() {
        viewPortX++;
        if( viewPortX > mapWidth-viewPortWidth ) {
            viewPortX = mapWidth-viewPortWidth;
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Texture getLoadTexture() {
        return loadTexture;
    }

    public void setLoadTexture(Texture loadTexture) {
        this.loadTexture = loadTexture;
    }

    public int getViewScrollSize() {
        return viewScrollSize;
    }

    public void setViewScrollSize(int viewScrollSize) {
        this.viewScrollSize = viewScrollSize;
    }

    public boolean isMouseScrollEnable() {
        return mouseScrollEnable;
    }

    public void setMouseScrollEnable(boolean mouseScrollEnable) {
        this.mouseScrollEnable = mouseScrollEnable;
    }

    public int getMouseScrollSpeed() {
        return mouseScrollSpeed;
    }

    public int getKeyScrollSpeed() {
        return keyScrollSpeed;
    }
    
    public boolean isLeftKeyScrollEnable() {
        return leftKeyScrollEnable;
    }

    public void setLeftKeyScrollEnable(boolean leftKeyScrollEnable) {
        this.leftKeyScrollEnable = leftKeyScrollEnable;
    }

    public boolean isRightKeyScrollEnable() {
        return rightKeyScrollEnable;
    }

    public void setRightKeyScrollEnable(boolean rightKeyScrollEnable) {
        this.rightKeyScrollEnable = rightKeyScrollEnable;
    }

    public boolean isTopKeyScrollEnable() {
        return topKeyScrollEnable;
    }

    public void setTopKeyScrollEnable(boolean topKeyScrollEnable) {
        this.topKeyScrollEnable = topKeyScrollEnable;
    }

    public boolean isDownKeyScrollEnable() {
        return downKeyScrollEnable;
    }

    public void setDownKeyScrollEnable(boolean downKeyScrollEnable) {
        this.downKeyScrollEnable = downKeyScrollEnable;
    }

    @Override
    public String toString() {
        return "GameMap{" + "name=" + name + ", description=" + description +
                ", loadTexture=" + loadTexture + 
                ", mapWidth=" + mapWidth + ", mapHeight=" + mapHeight + 
                ", mouseScrollEnable=" + mouseScrollEnable + ", mouseScrollSpeed=" + mouseScrollSpeed + 
                ", leftKeyScrollEnable=" + leftKeyScrollEnable + ", rightKeyScrollEnable=" + rightKeyScrollEnable + 
                ", topKeyScrollEnable=" + topKeyScrollEnable + ", downKeyScrollEnable=" + downKeyScrollEnable + ", keyScrollSpeed=" + keyScrollSpeed + '}';
    }
    
}
